# 目的：将一个整数转换为合适显示的十进制字符串
# 
# 输入：一个足够大的缓冲区，足以存放可能的最大数
#      一个有待转换的整数
#
# 输出：缓冲区将被十进制字符串覆盖
#
# 变量：%rcx将保存已处理的字符串
#      %rax将保存当前值
#      %rdi将保存基数（10）

.equ ST_VALUE, 16
.equ ST_BUFFER, 24

.globl integer2string
.type integer2string, @function
integer2string:
    # 一般函数开始
    push    %rbp
    mov     %rsp, %rbp

    # 当前字符
    mov     $0, %rcx
    # 将值移动到所需位置
    mov     ST_VALUE(%rbp), %rax
    # 当除以10时，数字10必须保存在寄存器中或内存位置中
    mov     $10, %rdi

conversion_loop:
    # 除法实际上时在%rdx:%rax两个寄存器上进行，因此首先清除%rdx
    mov     $0, %rdx
    # 将%rdx：%rax（这是默认的）除以10
    # 商存储在%rax中，余数存储在%rdx中（两者都是默认的）
    div     %rdi

    # 商是在正确的位置上，%rdx中含有余数，现在需要将其转换成数字
    # 因此，%rdx中有一个0-9的数字，你也可以认为该数字是ASCII表
    # 上从字符0开始的索引。0的ASCII码加上0仍然得到0的ASCII码，0的
    # ASCII码加上1为字符1的ASCII码。因此，以下指令将会让我们获得
    # %rsx中数字对应的ASCII码
    add     $'0', %rdx

    # 现在，我们将这个值入栈。这样，完成转换后就能以正确顺序逐个弹出
    # 字符。注意，我们是将整个寄存器入栈，但字符只需要寄存器%dl部分的字节
    push    %rdx
    # 递增数位计数
    inc     %rcx

    # 检查%rax是否为0，若为0则执行下一条指令
    cmp     $0, %rax
    je      end_conversion_loop

    # %rax中已经有了新值
    jmp     conversion_loop

end_conversion_loop:
    # 现在整个字符串都在栈中，如果我们一次弹出一个字符，
    # 就能将其复制带缓冲区中并完成任务

    # 获取%rdx中指向缓冲区的指针
    mov    ST_BUFFER(%rbp), %rdx

copy_reversing_loop:
    # 我们将整个寄存器入栈，但只需要寄存器的最后一个字节
    # 因此，我们需要弹出字符到整个%rax寄存器。但随后只移动
    # %rax寄存器的低位部分（%al）到字符串
    pop     %rax
    movb    %al, (%rdx)
    # 递减%rcx，这样我们就能判断是否完成
    dec     %rcx
    # 递增%rdx，使指针指向下一个字节
    inc     %rdx
    
    # 检查是否完成
    cmp     $0, %rcx
    je      end_copy_reversing_loop
    jmp     copy_reversing_loop

end_copy_reversing_loop:
    # 完成复制，现在写入一个空字节，并返回。
    movb    $0, (%rdx)
    mov     %rbp, %rsp
    pop     %rbp
    ret
